﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Reflection;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Linq.Expressions;
using WxPetaPoco;

namespace WxCarroit
{
    public class WxSdkEvan
    {
        Database database;
        public WxSdkEvan()
        {
            database = new Database("WxConnectionString");
        }
        public string EscapeTableName(string str)
        {
            return database.EscapeTableName(str);
        }

        public WxSdkEvan(string connectionStringName)
        {
            database = new Database(connectionStringName);
        }


        /// <summary>
        /// 是否存在该记录
        /// </summary>
        public virtual bool Exists<T>(object Id)
        {
            return database.Exists<T>(Id);
        }

        public virtual bool Exists<T>(string where, params object[] param)
        {
            var pd = WxPetaPoco.Database.PocoData.ForType(typeof(T));
            var d = database.ExecuteScalar<int>(string.Format("select count(0) from {1} where {0}", where, database.EscapeTableName(pd.TableInfo.TableName)), param);
            return d > 0;
        }

        /// <summary>
        /// 增加一条数据
        /// </summary>
        public virtual object Add<T>(T model)
        {
            return database.Insert(model);
        }

        /// <summary>
        /// 更新一条数据
        /// </summary>
        public virtual object Update<T>(T model)
        {
            return database.Update(model);
        }
        public int UpdateField<T>(object PrimaryKey, string setsql, params object[] args)
        {
            return database.UpdateField<T>(PrimaryKey, setsql, args);
        }

        public int UpdateField<T>(string wheresql, string setsql, params object[] args)
        {
            return database.UpdateField<T>(wheresql, setsql, args);
        }

        /// <summary>
        /// 删除一条数据
        /// </summary>
        public virtual int Delete<T>(int Id)
        {

            return database.Delete<T>(Id);
        }

        /// <summary>
        /// 得到一个对象实体
        /// </summary>
        public virtual T GetModel<T>(int Id)
        {

            return database.SingleOrDefault<T>(Id);
        }

        public virtual T GetModel<T>(string where, params object[] param)
        {
            return database.FirstOrDefault<T>(where, param);
        }

        public virtual int Delete<T>(string where, params object[] param)
        {
            return database.Delete<T>(where, param);
        }

        public virtual int Delete<T>(int id, params object[] param)
        {
            return database.Delete<T>("id=" + id, param);
        }

        public virtual IEnumerable<T> Query<T>(string Sql, params object[] param)
        {
            return database.Query<T>(Sql, param);
        }
        public virtual List<T> Fetch<T>(string Sql, params object[] param)
        {
            return database.Fetch<T>(Sql, param);
        }
        public virtual IEnumerable<T> Query<T>(int top, string Where, params object[] param)
        {
            var pd = WxPetaPoco.Database.PocoData.ForType(typeof(T));

            return database.Query<T>(string.Format("select {2} * from {0} where {1}", database.EscapeTableName(pd.TableInfo.TableName), Where, top > 0 ? " top " + top : string.Empty), param);
        }

        public virtual List<T> Fetch<T>(int top, string Where, params object[] param)
        {
            var pd = WxPetaPoco.Database.PocoData.ForType(typeof(T));

            return database.Fetch<T>(string.Format("select {2} * from {0} where {1}", database.EscapeTableName(pd.TableInfo.TableName), Where, top > 0 ? " top " + top : string.Empty), param);
        }

        public virtual IEnumerable<object> Query(int top, string Where, Type type, params object[] param)
        {
            var pd = WxPetaPoco.Database.PocoData.ForType(type);

            return database.Query(string.Format("select {2} * from {0} where {1}",
                database.EscapeTableName(pd.TableInfo.TableName),
                Where,
                top > 0 ? " top " + top : string.Empty),
                type, param);
        }

        public virtual IEnumerable<T> Query<T>(int top, string Where, string Order, params object[] param)
        {
            var pd = WxPetaPoco.Database.PocoData.ForType(typeof(T));
            return database.Query<T>(string.Format("select {2} * from {0} where {1} order by {3}", database.EscapeTableName(pd.TableInfo.TableName), Where, top > 0 ? " top " + top : string.Empty, Order), param);
        }
        public virtual Page<T> Page<T>(long pageSize, long currentPage, string strWhere, string filedOrder, params object[] param)
        {
            strWhere = string.Format(" where {0} order by {1}", strWhere, filedOrder);
            return database.Page<T>(currentPage, pageSize, strWhere, param);
        }


    }

    public static class Extend
    {
        #region GetSetValue
        public static object GetValue(this PropertyInfo p, object model)
        {
            var paramobj = Expression.Parameter(typeof(object), "Tmodel");

            var paramTmodel = Expression.Convert(paramobj, model.GetType());

            var pGetter = Expression.Property(paramTmodel, p);

            var returnObj = Expression.Convert(pGetter, typeof(object));

            var ExpressionLambda = Expression.Lambda<Func<object, object>>(returnObj, paramobj).Compile();

            return ExpressionLambda(model);
        }
        public static object GetValue<TModel>(this PropertyInfo p, TModel model)
        {
            var paramTmodel = Expression.Parameter(typeof(TModel), "Tmodel");

            var pGetter = Expression.Property(paramTmodel, p);

            var returnObj = Expression.Convert(pGetter, typeof(object));

            var ExpressionLambda = Expression.Lambda<Func<TModel, object>>(returnObj, paramTmodel).Compile();

            return ExpressionLambda(model);
        }


        public static void SetValue<TModel>(this PropertyInfo p, TModel model, object value)
        {
            var instanceParam = Expression.Parameter(typeof(TModel), "instance");

            var valueParam = Expression.Parameter(typeof(object), "value");

            var ConvertValueParam = Expression.Convert(valueParam, value.GetType());

            var propertyProperty = Expression.Property(instanceParam, p);

            var assignExpression = Expression.Assign(propertyProperty, ConvertValueParam);

            var lambdaExpression = Expression.Lambda<Action<TModel, object>>(assignExpression, instanceParam, valueParam).Compile();

            lambdaExpression(model, value);
        }

        public static void SetValue(this PropertyInfo p, object model, object value, Type t)
        {
            var instanceParam = Expression.Parameter(typeof(object), "instance");

            var convertInstanceParam = Expression.Convert(instanceParam, t);

            var valueParam = Expression.Parameter(typeof(object), "value");

            var ConvertValueParam = Expression.Convert(valueParam, value.GetType());

            var propertyProperty = Expression.Property(convertInstanceParam, p);

            var assignExpression = Expression.Assign(propertyProperty, ConvertValueParam);

            var lambdaExpression = Expression.Lambda<Action<object, object>>(assignExpression, instanceParam, valueParam).Compile();

            lambdaExpression(model, value);
        }
        #endregion
    }
    /// <summary>
    /// 摘自http://blog.zhaojie.me/2008/11/invoke-method-by-lambda-expression.html
    /// </summary>
    public static class DynamicMethodExecutor
    {
        #region DynamicMethodExecutor
        private static readonly Hashtable htDM = Hashtable.Synchronized(new Hashtable(10240));

        /// <summary>
        /// 实例范型方法/实例静态范型方法调用
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <param name="instance"></param>
        /// <param name="parameters"></param>
        public static object Execute(this MethodInfo methodInfo, object instance, Type[] t, params object[] parameters)
        {
            var m_execute = GetExecuteDelegate(methodInfo, t);
            return m_execute(instance, parameters);
        }

        /// <summary>
        /// 实例方法/实例静态方法调用
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <param name="instance"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static object Execute(this MethodInfo methodInfo, object instance, params object[] parameters)
        {
            var m_execute = GetExecuteDelegate(methodInfo, null);
            return m_execute(instance, parameters);
        }

        /// <summary>
        /// 静态类方法/范型方法调用
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <param name="t"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static object Execute(this MethodInfo methodInfo, Type[] t, params object[] parameters)
        {
            var m_execute = GetStaticExecuteDelegate(methodInfo, t);
            return m_execute(parameters);
        }

        private static Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo, Type[] t)
        {
            Func<object, object[], object> res = (Func<object, object[], object>)htDM[methodInfo];

            if (res == null)
            {
                ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance");

                ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");

                // build parameter list
                List<Expression> parameterExpressions = new List<Expression>();

                ParameterInfo[] paramInfos = methodInfo.GetParameters();

                for (int i = 0; i < paramInfos.Length; i++)
                {
                    BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));

                    UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);

                    parameterExpressions.Add(valueCast);
                }

                Expression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceParameter, methodInfo.ReflectedType);

                var methodCall = Expression.Call(instanceCast, methodInfo.Name, t, parameterExpressions.ToArray());


                if (methodCall.Type == typeof(void))
                {
                    Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceParameter, parametersParameter);

                    Action<object, object[]> execute = lambda.Compile();
                    res = (instance, parameters) =>
                    {
                        execute(instance, parameters);
                        return null;
                    };
                }
                else
                {
                    UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
                    Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceParameter, parametersParameter);

                    res = lambda.Compile();
                }
            }

            htDM[methodInfo] = res;

            return res;
        }

        private static Func<object[], object> GetStaticExecuteDelegate(MethodInfo methodInfo, Type[] t)
        {
            Func<object[], object> res = (Func<object[], object>)htDM[methodInfo];

            if (res == null)
            {

                ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");

                // build parameter list
                List<Expression> parameterExpressions = new List<Expression>();
                ParameterInfo[] paramInfos = methodInfo.GetParameters();
                for (int i = 0; i < paramInfos.Length; i++)
                {
                    // (Ti)parameters[i]
                    BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
                    UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);
                    parameterExpressions.Add(valueCast);
                }

                // static invoke or ((TInstance)instance).Method
                MethodCallExpression methodCall = Expression.Call(methodInfo.ReflectedType, methodInfo.Name, t, parameterExpressions.ToArray());

                // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)
                if (methodCall.Type == typeof(void))
                {
                    var execute = Expression.Lambda<Action<object[]>>(methodCall, parametersParameter).Compile();

                    res = (parameters) => { execute(parameters); return null; };
                }
                else
                {
                    UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
                    Expression<Func<object[], object>> lambda = Expression.Lambda<Func<object[], object>>(castMethodCall, parametersParameter);

                    res = lambda.Compile();
                }
            }
            return res;
        }

        #endregion}

        #region Attribute
        /// <summary>
        /// 可序列化属性
        /// </summary>
        public class SerialAttribute : Attribute { }

        /// <summary>
        /// 非数据库字段属性
        /// </summary>
        public class NotDBFieldAttribute : Attribute { }

        #endregion
    }
    public class NotDBFieldAttribute : Attribute
    { }
    public class SerialAttribute : Attribute
    {
        public SerialAttribute(string t)
        {
            this.Title = t;
        }
        private string _title;
        public string Title { get { return _title; } set { _title = value; } }
    }
    public class ExcelAttribute : Attribute
    {
        public ExcelAttribute(string t)
        {
            this.Title = t;
        }
        private string _title;
        public string Title { get { return _title; } set { _title = value; } }
    }

}
